/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package control;

import entidad.Movimiento;
import Distribuciones.Distribucion;
import ec.util.MersenneTwisterFast;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.util.ArrayList;

/**
 *
 * @author julianPG
 */
public class OptimizacionInventario {

    private static int condicionParada = 500;
    private static int tamanioTabu = 14;
    private static double tamanioMovimiento = 50D;
    private static ArrayList<Movimiento> listaTabu;
    private static int numeroPruebas = 10;
    private static int diasEnAnio = 250;
    private final static MersenneTwisterFast prng = new MersenneTwisterFast(321654987L);

    public static Object[] OptimizarQyS(Distribucion demanda, Distribucion tiempoEntrega, double costoUnitario, double costoPedido, double costoMantenimiento, double costoFaltante, Canvas canvas) {
        inicializarListaTabu();
        Object[] res = new Object[3];
        int[] X = new int[condicionParada];
        int[] Y = new int[condicionParada];
        for (int i = 0; i < Y.length; i++) {
            X[i] = 0;
            Y[i] = 0;
            
        }
        int Q, S;
        Q = determinarQInicial(demanda, tiempoEntrega, costoPedido, costoMantenimiento);
        S = determinarSInicial(Q, demanda, costoMantenimiento, costoFaltante);
        double costoOptimoInventario = calcularCostoInventario(demanda, tiempoEntrega, costoUnitario, costoPedido, costoMantenimiento, costoFaltante, Q, S);
        double costoOptimoLocal;
        double costoInventario;
        int QOptimoLocal = 0, SOptimoLocal = 0, q, s, QMedio = Q, SMedio = S;
        for (int i = 0; i < condicionParada; i++) {
            costoOptimoLocal = Double.MAX_VALUE;
            //TODO revisar logica
            for (int j = 0; j < numeroPruebas; j++) {
                double radian = (2 * Math.PI) * prng.nextDouble();
                q = (int) (QMedio + Math.round(tamanioMovimiento * Math.cos(radian)));
                s = (int) (SMedio + Math.round(tamanioMovimiento * Math.sin(radian)));
                if (q < 1 || s < 1) {
                    continue;
                }
                Movimiento movimiento = new Movimiento(q, s);
                boolean esta = movimientoEstaEnListaTabu(movimiento);
                if (esta) {
                    continue;
                }
                costoInventario = calcularCostoInventario(demanda, tiempoEntrega, costoUnitario, costoPedido, costoMantenimiento, costoFaltante, q, s);
                if (costoInventario <= costoOptimoLocal) {
                    QOptimoLocal = q;
                    SOptimoLocal = s;
                    costoOptimoLocal = costoInventario;
                }
            }
            if (costoOptimoLocal <= costoOptimoInventario) {
                Q = QOptimoLocal;
                S = SOptimoLocal;
                costoOptimoInventario = costoOptimoLocal;
            }
            Movimiento m = new Movimiento(new Integer(QMedio), new Integer(SMedio));
            agregarMovimientoAListaTabu(m);
            QMedio = QOptimoLocal;
            SMedio = SOptimoLocal;
            X[i] = QOptimoLocal;
            Y[i] = SOptimoLocal;
        }
//        System.out.println("Q= " + Q + " S= " + S + " costo = " + costoOptimoInventario);
        res[0] = Q;
        res[1] = S;
        res[2] = costoOptimoInventario;
        displayXY(X,Y,Q,S,canvas);
        return res;
    }

    public static double calcularCostoInventario(Distribucion demanda, Distribucion tiempoEntrega, double costoUnitario, double costoPedido, double costoMantenimiento, double costoFaltante, int Q, int S) {
        /* variables de control */
//        NumberFormat format = NumberFormat.getCurrencyInstance();
        int demandaPerdida;
        boolean pedido;
        int inventarioInicial;
        int inventarioFinal;
        int diasEntrega;
        double costoAlmacenamientoAnual;
        double costo = 0;
        int sumaDemandaPerdida = 0;
        int sumaDemandaPerdida2 = 0;
        diasEntrega = -1;
        inventarioInicial = Q;
        inventarioFinal = Q;
        demandaPerdida = 0;
        costoAlmacenamientoAnual = 0;
        int count = 0;
        pedido = false;
        /*simulacion de un inventario en un anio*/
        for (int j = 1; j <= diasEnAnio; j++) {
            int di = (int) Math.round(demanda.generar(prng.nextDouble()));
            if (inventarioFinal < di) {
                demandaPerdida += di - inventarioFinal;
                inventarioFinal = 0;
            } else {
                inventarioFinal = inventarioInicial - di;
            }
            if ((j % S == 0) && !pedido) {
                pedido = true;
                count++;
                diasEntrega = (int) Math.round(tiempoEntrega.generar(prng.nextDouble()));
            }
            if (pedido) {
                if (diasEntrega == 0) {
                    inventarioFinal += Q;
                    pedido = false;
                }
                diasEntrega--;
            }

            costoAlmacenamientoAnual += (inventarioFinal * costoMantenimiento);
            inventarioInicial = inventarioFinal;
        }
        double costoUnidadesPerdidas = demandaPerdida * costoFaltante;
        double costoPedidos = count * (costoPedido + (Q * costoUnitario));
        costo = costoAlmacenamientoAnual + costoUnidadesPerdidas + costoPedidos;
        sumaDemandaPerdida += demandaPerdida;
        sumaDemandaPerdida2 += demandaPerdida * demandaPerdida;
//        System.out.println("Q= " + Q + " S= " + S);
//        System.out.println("Pedidos realizados: " + count);
//        System.out.println("Costo Preparacion de lotes: " + format.format(costoPedidos));
//        System.out.println("costo Anual de almacenamiento: " + format.format(costoAlmacenamientoAnual));
//        System.out.println("Demanda perdida Anual: " + demandaPerdida);
//        System.out.println("Costo de la demanda perdida anual: " + format.format(costoUnidadesPerdidas));
//        System.out.println("Costo total anual: " + format.format(costo));
//        System.out.println("-------------------------------------");
        return costo;

    }

    private static int determinarQInicial(Distribucion demanda, Distribucion tiempoEntrega, double costoPedido, double costoMantenimiento) {
        double mediaDemanda = demanda.calcularMedia();
        return (int) Math.round(Math.sqrt(2*mediaDemanda*costoPedido/costoMantenimiento));
    }

    private static int determinarSInicial(int Q, Distribucion demanda, double costoMantenimiento, double costoFaltante) {
        double mediaDemanda = demanda.calcularMedia();
        return (int) Math.min(Math.round((Q* (Math.sqrt(costoFaltante/costoMantenimiento))) / mediaDemanda),diasEnAnio);
    }

    private static void inicializarListaTabu() {
        listaTabu = new ArrayList<Movimiento>();
    }

    private static boolean movimientoEstaEnListaTabu(Movimiento movimiento) {
        for (Movimiento m : listaTabu) {
            if (m.equals(movimiento)) {
                return true;
            }
        }
        return false;
    }

    private static void agregarMovimientoAListaTabu(Movimiento m) {
        if (listaTabu.size() >= tamanioTabu) {
            listaTabu.remove(0);
        }
        listaTabu.add(m);
    }

    private static void displayXY(int[] X, int[] Y, int Q, int S, Canvas canvas) {

        int width = canvas.getWidth();
        int heigth = canvas.getHeight();
        int maxY = 0;
        int minY = Integer.MAX_VALUE;
        for (int i : Y) {
            if (i > maxY) {
                maxY = i;
            }
            if (i < minY) {
                minY = i;
            }
        }
        int heigthDatos = maxY - minY;
        int maxX = 0;
        int minX = Integer.MAX_VALUE;
        for (int i : X) {
            if (i > maxX) {
                maxX = i;
            }
            if (i < minX) {
                minX = i;
            }
        }
        int widthDatos = maxX - minX;
        double razonX = (width*1D)/(widthDatos*1D);
        double razonY = (heigth*1D)/(heigthDatos*1D);
        Color color;
        Graphics gr = canvas.getGraphics();
        gr.clearRect(0, 0, width, heigth);
        
        for (int i = 1; i < Y.length -1; i++) {
            color = new Color((i)%256,(i*2)%256,255);
            gr.setColor(color);
            gr.fillRect((int)((X[i] - minX)*razonX), (int)((Y[i] - minY)*razonY),7, 7);
        }
        color = new Color(255,0,0);
        gr.setColor(color);
        gr.fillOval((int)((X[0] - minX)*razonX), (int)((Y[0] - minY)*razonY),10, 10);
        gr.fillRect((int)((X[Y.length -1] - minX)*razonX), (int)((Y[Y.length -1] - minY)*razonY),10, 10);
        color = new Color(0,255,0);
        gr.setColor(color);
        gr.fillRect((int)(Q*razonX) - minX, (int)(S*razonY) - minY,10, 10);
        color = new Color(255,0,0);
        gr.setColor(color);
        gr.setFont(new Font("Arial",1, 18));
        gr.drawString("min S " + String.valueOf(minY), width-130, 35);
        gr.drawString("min Q " + String.valueOf(minX), width-130, 15);
        gr.drawString("max S " + String.valueOf(maxY), width-130, heigth - 10);
        gr.drawString("max Q " + String.valueOf(maxX), width-130, heigth - 30);
    }
}
